package xgo

import (
	"context"
	"fmt"
	"github.com/pkg/errors"
	"go.uber.org/zap"
	"runtime"
	"tapay-ws/global"
	"tapay-ws/internal/model"
	"tapay-ws/pkg/util/xstring"
)

// Go goroutine
func Go(fn func()) {
	//nolint: errcheck
	go try2(fn, nil)
}

func GoWithError(fn func() error) error {
	return tryWithError(fn, nil)
}

func GoWithErrorParams(fn func() error, params interface{}, selectKey string) error {
	return tryWithErrorParams(fn, nil, params, selectKey)
}

func LogErr(fn func() error, params interface{}) (ret error) {
	ret = fn()
	if ret != nil {
		errLog := model.ErrorLog{}
		errLog.CreateErrLog2(global.DBEngine, 0, xstring.FunctionName(fn), params, fmt.Sprintf("%+v", ret), "")
	}
	return ret
}

func tryWithError(fn func() error, cleaner func()) (ret error) {
	return tryWithErrorParams(fn, cleaner, nil, "")
}

func tryWithErrorParams(fn func() error, cleaner func(), params interface{}, selectKey string) (ret error) {
	if cleaner != nil {
		defer cleaner()
	}
	errLog := model.ErrorLog{}
	defer func() {
		_, file, line, _ := runtime.Caller(5)

		if err := recover(); err != nil {
			if _, ok := err.(error); ok {
				ret = err.(error)
			} else {
				ret = fmt.Errorf("%+v", err)
			}
			funcName := xstring.FunctionName(fn)
			ret = errors.Wrap(ret, fmt.Sprintf("%s:%d", funcName, line))
			errMsg := fmt.Sprintf("recover %+v %+v", zap.Any("err", ret), zap.String("line", fmt.Sprintf("%s:%d", file, line)))
			errLog.CreateErrLog2(global.DBEngine, 0, xstring.FunctionName(fn), params, errMsg, selectKey)
		}
	}()
	ret = fn()
	if ret != nil {
		errLog.CreateErrLog2(global.DBEngine, 0, xstring.FunctionName(fn), params, fmt.Sprintf("%+v", ret), selectKey)
	}
	return ret
}

func try2(fn func(), cleaner func()) (ret error) {
	if cleaner != nil {
		defer cleaner()
	}
	defer func() {
		_, file, line, _ := runtime.Caller(5)
		if err := recover(); err != nil {
			global.Logger.Error(context.Background(), "recover", zap.Any("err", err), zap.String("line", fmt.Sprintf("%s:%d", file, line)))
			if _, ok := err.(error); ok {
				ret = err.(error)
			} else {
				ret = fmt.Errorf("%+v", err)
			}
		}
	}()
	fn()
	return nil
}
